package com.piece.core.framework.util.security.coder;

import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.util.basic.I18nUtil;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA安全编码组件
 */
public class RSACoder {

    /**
     * 数字签名 密钥算法
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 数字签名 签名/验证算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    public static final String SIGNATURE_ALGORITHM_SHA256 = "SHA256WithRSA";

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * RSA密钥长度 默认1024位， 密钥长度必须是64的倍数， 范围在512至65536位之间。
     */
    private static final int KEY_SIZE = 1024;

    /**
     * 生成RAS公钥与私钥字符串
     */
    public static Map<String, String> initKey() throws Exception {
        // 实例化密钥对儿生成器
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        // 初始化密钥对儿生成器
        keyPairGen.initialize(KEY_SIZE);
        // 生成密钥对儿
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //得到公钥字符串
        String publicKeyStr = new String(Base64.encodeBase64(publicKey.getEncoded()));
        //得到私钥字符串
        String privateKeyStr = new String(Base64.encodeBase64(privateKey.getEncoded()));
        // 封装密钥
        Map<String, String> keyMap = new HashMap<>(2);
        keyMap.put(PUBLIC_KEY, publicKeyStr);
        keyMap.put(PRIVATE_KEY, privateKeyStr);
        return keyMap;
    }

    /**
     * 从字符串中加载公钥
     */
    public static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decodeBase64(publicKeyStr.getBytes());
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new ServerException(I18nUtil.message(ExceptionConstants.RSA_METHOD));
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            throw new ServerException(I18nUtil.message(ExceptionConstants.RSA_PUBLIC_KEY));
        } catch (NullPointerException e) {
            e.printStackTrace();
            throw new ServerException(I18nUtil.message(ExceptionConstants.RSA_PUBLIC_KEY));
        }
    }

    /**
     * 从字符串中加载私钥
     */
    public static RSAPrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decodeBase64(privateKeyStr.getBytes());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new ServerException(I18nUtil.message(ExceptionConstants.RSA_METHOD));
        } catch (InvalidKeySpecException e) {
            throw new ServerException(I18nUtil.message(ExceptionConstants.RSA_PRIVATE_KEY));
        } catch (NullPointerException e) {
            throw new ServerException(I18nUtil.message(ExceptionConstants.RSA_PRIVATE_KEY));
        }
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        // 取私钥匙对象
        PrivateKey priKey = loadPrivateKey(privateKey);
        return sign(data, priKey);
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     */
    public static String sign(byte[] data, PrivateKey privateKey) throws Exception {
        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        // 初始化Signature
        signature.initSign(privateKey);
        // 更新
        signature.update(data);
        // 签名
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     */
    public static String sign256(byte[] data, String privateKey) throws Exception {
        // 取私钥匙对象
        PrivateKey priKey = loadPrivateKey(privateKey);
        return sign256(data, priKey);
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     */
    public static String sign256(byte[] data, PrivateKey privateKey) throws Exception {
        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA256);
        // 初始化Signature
        signature.initSign(privateKey);
        // 更新
        signature.update(data);
        // 签名
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 校验
     *
     * @param data      待校验数据
     * @param publicKey 公钥
     * @param sign      数字签名
     */
    public static boolean verify(byte[] data, PublicKey publicKey, String sign) throws Exception {
        // 生成公钥
        //PublicKey pubKey = loadPublicKey(publicKey);
        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        // 初始化Signature
        signature.initVerify(publicKey);
        // 更新
        signature.update(data);
        // 验证
        return signature.verify(Base64.decodeBase64(sign));
    }

    /**
     * 校验
     *
     * @param data      待校验数据
     * @param publicKey 公钥
     * @param sign      数字签名
     */
    public static boolean verify256(byte[] data, PublicKey publicKey, String sign) throws Exception {
        // 生成公钥
        //PublicKey pubKey = loadPublicKey(publicKey);
        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA256);
        // 初始化Signature
        signature.initVerify(publicKey);
        // 更新
        signature.update(data);
        // 验证
        return signature.verify(Base64.decodeBase64(sign));
    }

    /**
     * 取得私钥
     */
    public static byte[] getPrivateKey(Map<String, String> keyMap) throws Exception {
        Key key = loadPrivateKey(keyMap.get(PRIVATE_KEY));
        return key.getEncoded();
    }

    /**
     * 取得公钥
     */
    public static byte[] getPublicKey(Map<String, String> keyMap) throws Exception {
        Key key = loadPublicKey(keyMap.get(PUBLIC_KEY));
        return key.getEncoded();
    }

    /**
     * 私钥解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     */
    public static byte[] decryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        // 对数据解密
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥解密
     *
     * @param data      待解密数据
     * @param publicKey 公钥
     */
    public static byte[] decryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
        // 对数据解密
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     */
    public static byte[] encryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
        // 对数据加密
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 私钥加密
     *
     * @param data       待加密数据
     * @param privateKey 私钥
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        // 对数据加密
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
}
